package fruit_machine

import (
	"encoding/json"
	"errors"
	"gorm.io/gorm"
	global2 "longmen/server/config/global"
	_const "longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/models/models_main"
	"longmen/server/pkg/db/mysql/models/models_report"
	"longmen/server/pkg/db/redis/cache"
	pb "longmen/server/pkg/protobuf"
	"math/rand"
	"time"
)

type Fruits struct {
	Fruit            []*FruitConfig
	AwardTypes       []*AwardTypeConfig
	BetAreaConfig    []*BetAreaConfig
	TotalProbability float64
}

func NewFruits() *Fruits {
	fruit, totalProbability := fruitConfig()
	return &Fruits{
		Fruit:            fruit,
		AwardTypes:       fruitAwardTypeConfig(),
		TotalProbability: totalProbability,
		BetAreaConfig:    betAreaConfig(),
	}
}

func (f *Fruits) writeAwardHis(tx *gorm.DB, fruitGameHis *FruitGameHis) error {
	betData, _ := json.Marshal(fruitGameHis.BetData)
	awardData, _ := json.Marshal(fruitGameHis.AwardData)
	return tx.Create(&models_report.GameFruitFlow{
		Uid:           fruitGameHis.UserId,
		AwardAmount:   fruitGameHis.AwardAmount,
		BeforeBalance: fruitGameHis.BeforeBalance,
		AfterBalance:  fruitGameHis.AfterBalance,
		BetData:       string(betData),
		AwardType:     int(fruitGameHis.AwardType),
		AwardData:     string(awardData),
		Ctime:         fruitGameHis.Ctime,
	}).Error
}

// GameplayNormal 正常玩法
func (f *Fruits) GameplayNormal(userId int64, betData []*pb.FruitBetData) (*pb.FruitActionResp, error) {
	var totalBetAmount int64
	for _, v := range betData {
		totalBetAmount += v.GetAmount()
	}
	if totalBetAmount <= 0 {
		return nil, errors.New("下注金额不能为0")
	}
	userInfo, _ := cache.GetUserFullInfo(int(userId))
	if int64(userInfo.Gold) < totalBetAmount {
		return nil, errors.New(global2.Lang.T("insufficient_balance"))
	}

	var awardAmount int32
	var awardData []*pb.FruitAwardData
	odds := int32(0)
	awardRes, awardType := f.getAwardIds()
	var err error
	var awardAreaIds []int32
	awardAreaMap := make(map[int32]interface{})
	var totalMultiple int32
	for _, v := range awardRes {
		awardConfig := f.getAwardConfig(v.Idx)
		awardAreaMap[int32(awardConfig.BetId)] = struct{}{}
		if awardConfig.Idx > 0 {
			if awardConfig.MaxOdds-awardConfig.MinOdds == 0 {
				odds = awardConfig.MaxOdds
			} else {
				odds = int32(util.MaxMinRand(int(awardConfig.MinOdds), int(awardConfig.MaxOdds)))
			}
			for _, l := range betData {
				if l.Amount > 0 {
					//global2.Log.Debugf("下注数据是:%+v,awardConfig.Idx:%v,下注区:%v\n", l, awardConfig.Idx, awardConfig.BetId)
					if f.checkWinning(int(l.BetId), awardConfig.Idx) {
						awardAmount += odds * int32(l.GetAmount())
					}
				}
			}
			awardData = append(awardData, &pb.FruitAwardData{
				AwardId:  int32(awardConfig.Idx),
				Multiple: odds,
				BetId:    int32(awardConfig.BetId),
				Site:     v.Site,
			})
			totalMultiple += odds
		}
	}
	var afterAmount int
	actualPrizeAmount := int64(awardAmount) - totalBetAmount
	if actualPrizeAmount != 0 {
		var beforeAmount int
		err = global2.GetX().Transaction(func(tx *gorm.DB) error {
			profitLog := f.genProfit(int(userId), int(actualPrizeAmount))
			beforeAmount, afterAmount, err = db_main.UpdateAmountByTx(int(userId), int(actualPrizeAmount), _const.CoinGold, tx, _const.WALLET_BALANCE_SHOW, profitLog)
			if err != nil {
				return err
			}
			fruitGameHis := &FruitGameHis{
				UserId:        userId,
				BetData:       betData,
				AwardType:     awardType,
				AwardData:     awardData,
				Ctime:         time.Now().Unix(),
				AwardAmount:   int(awardAmount),
				BeforeBalance: beforeAmount,
				AfterBalance:  afterAmount,
			}
			cache.UpdateUserGoldCache(int(userId), int64(afterAmount))
			return f.writeAwardHis(tx, fruitGameHis)
		})
		if err != nil {
			return nil, err
		}
	} else {
		afterAmount = userInfo.Gold
	}

	for id, _ := range awardAreaMap {
		awardAreaIds = append(awardAreaIds, id)
	}
	resp := &pb.FruitActionResp{GameType: GameplayNormal, AwardType: int32(awardType), AwardAmount: awardAmount, Balance: int32(afterAmount), AwardData: awardData, BetAreaIds: awardAreaIds, Multiple: totalMultiple}
	return resp, nil
}

// GamePlayBs 大小玩法 目前没用到b vbggj hj
func (f *Fruits) GamePlayBs(userId int64, awardId int32, amount int64) (*pb.FruitActionResp, error) {
	multiple := int32(2)
	userInfo, _ := cache.GetUserFullInfo(int(userId))
	if int64(userInfo.Gold) < amount {
		return nil, errors.New(global2.Lang.T("insufficient_balance"))
	}
	var prizeAmount int32     //中奖金额
	var actualPrizeAmount int //实际金额
	respId := util.MaxMinRand(100, 101)

	if int32(respId) == awardId {
		prizeAmount = int32(amount) * multiple
		actualPrizeAmount = int(amount)
	} else {
		actualPrizeAmount = int(amount) * -1
	}
	var awardDatas []*pb.FruitAwardData
	awardDatas = append(awardDatas, &pb.FruitAwardData{
		AwardId:  int32(respId),
		Multiple: multiple,
	})
	profitLog := f.genProfit(int(userId), actualPrizeAmount)
	_, afterAmount, err := db_main.UpdateAmountByTx(int(userId), actualPrizeAmount, _const.CoinGold, global2.GetX(), _const.WALLET_BALANCE_SHOW, profitLog)
	if err != nil {
		return nil, err
	}
	cache.UpdateUserGoldCache(int(userId), int64(afterAmount))
	return &pb.FruitActionResp{
		AwardAmount: prizeAmount,
		GameType:    int32(AwardTypeNormal),
		AwardType:   awardId,
		AwardData:   awardDatas,
		Balance:     int32(afterAmount),
	}, nil
}

func (f *Fruits) checkWinning(betId int, awardId int) bool {
	for _, v := range f.BetAreaConfig {
		if v.BetId == betId {
			for _, id := range v.AwardIds {
				if id == awardId {
					return true
				}
			}
		}
	}
	return false
}

// 生成用户消费流水
func (f *Fruits) genProfit(userId int, coinCount int) []*models_main.UserProfit {
	userProfit := &models_main.UserProfit{
		Uid:         userId,
		CoinCount:   coinCount,
		Content:     util.GetConsumeName(25),
		Type:        _const.FINANCE_DEBIT,
		ConsumeType: _const.GAME_FRUIT_BET,
		Resid:       0,
		Count:       1, //数量为1
		CreateTime:  time.Now(),
		CoinType:    _const.FEE_TYPE_GOLD,
		FUid:        0, //没有收款人，所以前台不显示收款人是谁
		OrderNo:     util.GenOrderId("FRUIT", 1),
		Liveid:      0,
	}
	var profitLog []*models_main.UserProfit
	profitLog = append(profitLog, userProfit)
	return profitLog
}

func (f *Fruits) getAwardIds() ([]*RewardResult, AwardType) {
	var awardIds []*RewardResult
	awardType := f.getAwardType()
	switch awardType {
	case AwardTypeNormal:
		awardIds = f.getAwardTypeNormal()
	case AwardTypeTrain:
		awardIds = f.getAwardTypeTrain(6)
	case AwardTypeDsy:
		awardIds = f.getAwardTypeDsy()
	case AwardTypeXsy:
		awardIds = f.getAwardTypeXsy()
	case AwardTypeMtx:
		awardIds = f.getAwardTypeMtx(8)
	case AwardTypeFullHouse:
		awardIds = f.getAwardTypeFullHouse()
	case AwardTypeGrandSlam:
		awardIds = f.getAwardTypeGrandSlam()
	default:
		awardIds = f.getAwardTypeNormal()
	}
	return awardIds, awardType
}

func (f *Fruits) getAwardTypeNormal() []*RewardResult {
	var awardRes []*RewardResult
	r := rand.Float64() * f.TotalProbability
	for _, v := range f.Fruit {
		if r < v.CumProbability {
			awardRes = append(awardRes, &RewardResult{
				Idx:  v.Idx,
				Site: v.Site[0],
			})
			break
		}
	}
	return awardRes
}

// 开火车
func (f *Fruits) getAwardTypeTrain(maxAwardNum int) []*RewardResult {
	var awardRes []*RewardResult
	var sites []int32
	trainLen := util.MaxMinRand(2, maxAwardNum)
	r := rand.Float64() * f.TotalProbability
	found := false
	siteToAwardIdMap := make(map[int32]int)
	for _, v := range f.Fruit {
		for _, site := range v.Site {
			siteToAwardIdMap[site] = v.Idx
		}
		if !found {
			if r < v.CumProbability {
				for _, site := range v.Site {
					found = true
					for i := 0; i < trainLen; i++ {
						if site > MaxSite {
							site = 1
						}
						sites = append(sites, site)
						site++
					}
					break
				}
			}
		}
	}
	for _, site := range sites {
		awardRes = append(awardRes, &RewardResult{
			Idx:  siteToAwardIdMap[site],
			Site: site,
		})
		for _, v := range f.Fruit {
			if v.Idx == siteToAwardIdMap[site] {
				break
			}
		}

	}

	return awardRes
}

// 满天星玩法
func (f *Fruits) getAwardTypeMtx(maxAwardNum int) []*RewardResult {
	var awardRes []*RewardResult
	awardMap := make(map[int]*FruitConfig)
	trainLen := util.MaxMinRand(2, maxAwardNum)
	for i := 0; i < trainLen; i++ {
		r := rand.Float64() * f.TotalProbability
		for _, v := range f.Fruit {
			if r < v.CumProbability {
				if _, ok := awardMap[v.Idx]; !ok {
					awardMap[v.Idx] = v
					awardRes = append(awardRes, &RewardResult{
						Idx:  v.Idx,
						Site: v.Site[0],
					})
					break
				}
			}
		}
	}
	return awardRes
}

// 大三元
func (f *Fruits) getAwardTypeDsy() []*RewardResult {
	var awardRes []*RewardResult
	awardIds := []int{3, 5, 7}
	for _, id := range awardIds {
		for _, v := range f.Fruit {
			if v.Idx == id {
				awardRes = append(awardRes, &RewardResult{
					Idx:  v.Idx,
					Site: v.Site[0],
				})
				break
			}
		}
	}

	return awardRes
}

// 小三元
func (f *Fruits) getAwardTypeXsy() []*RewardResult {
	var awardRes []*RewardResult
	awardIds := []int{9, 11, 13}
	for _, id := range awardIds {
		for _, v := range f.Fruit {
			if v.Idx == id {
				awardRes = append(awardRes, &RewardResult{
					Idx:  v.Idx,
					Site: v.Site[0],
				})
				break
			}
		}
	}
	return awardRes
}

// 同奖全中
func (f *Fruits) getAwardTypeFullHouse() []*RewardResult {
	var awardRes []*RewardResult
	var samePrize []int
	r := rand.Float64() * f.TotalProbability
	for _, v := range f.Fruit {
		if r < v.CumProbability {
			samePrize = v.SamePrize
			break
		}
	}
	for _, id := range samePrize {
		for _, l := range f.Fruit {
			if id == l.Idx {
				awardRes = append(awardRes, &RewardResult{
					Idx:  id,
					Site: l.Site[0],
				})
				break
			}
		}
	}
	return awardRes
}

// 大满贯
func (f *Fruits) getAwardTypeGrandSlam() []*RewardResult {
	var awardRes []*RewardResult
	for _, v := range f.Fruit {
		awardRes = append(awardRes, &RewardResult{
			Idx:  v.Idx,
			Site: v.Site[0],
		})
	}
	return awardRes
}
